package com.laf.manage.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.laf.common.constant.CacheConstants;
import com.laf.common.core.domain.entity.MobileUser;
import com.laf.common.core.domain.model.LoginMobileUser;
import com.laf.common.core.page.TableDataInfo;
import com.laf.common.core.redis.RedisCache;
import com.laf.common.exception.ServiceException;
import com.laf.common.utils.SecurityUtils;
import com.laf.manage.domain.doc.DynamicDoc;
import com.laf.manage.domain.dto.*;
import com.laf.manage.domain.po.Dynamic;
import com.laf.manage.domain.po.DynamicTag;
import com.laf.manage.domain.po.Tag;
import com.laf.manage.domain.query.DynamicQuery;
import com.laf.manage.domain.vo.DynamicVO;
import com.laf.manage.enums.VerifyStatusEnum;
import com.laf.manage.mapper.es.DynamicEsMapper;
import com.laf.manage.mapper.mp.CommentsMapper;
import com.laf.manage.mapper.mp.DynamicMapper;
import com.laf.manage.mapper.mp.StoreMapper;
import com.laf.manage.mapper.mp.TagMapper;
import com.laf.manage.service.IDynamicService;
import com.laf.manage.service.IDynamicTagService;
import com.laf.mobile.service.IMobileUserService;
import lombok.extern.slf4j.Slf4j;
import org.dromara.easyes.core.biz.EsPageInfo;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 内容管理Service业务层处理
 *
 * @author zian
 * @date 2024-11-09
 */
@Slf4j
@Service
public class DynamicServiceImpl extends ServiceImpl<DynamicMapper, Dynamic> implements IDynamicService {

    @Autowired
    private DynamicEsMapper dynamicEsMapper;

    @Autowired
    private TagMapper tagMapper;

    @Autowired
    private DynamicEsService dynamicEsService;

    @Autowired
    private IDynamicTagService dynamicTagService;

    @Autowired
    private IMobileUserService userService;

    @Autowired
    private CommentsMapper commentsMapper;

    @Autowired
    private StoreMapper storeMapper;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private DynamicMapper dynamicMapper;

    /**
     * 初始化方法，用于将存储动态和浏览量的数据同步到redis中
     */
    @PostConstruct
    public void init() {
        // 当浏览量缓存不存在的时候才进行缓存操作
        log.info("开始初始化动态缓存");
        List<Dynamic> dynamics = dynamicMapper.selectList(
                Wrappers.<Dynamic>lambdaQuery()
                        .in(Dynamic::getVerifyStatus, VerifyStatusEnum.VERIFY_PASS, VerifyStatusEnum.WAIT_VERIFY)
                        .select(Dynamic::getId, Dynamic::getHits)
        );
        List<Long> ids = dynamics.stream().map(Dynamic::getId).collect(Collectors.toList());
        // 获取动态对应的收藏数
        List<DynamicCountDTO> storeCountByDynamicIds = storeMapper.dynamicStoreCountByDynamicIds(ids);
        Map<String, Integer> storeMap = storeCountByDynamicIds.stream().collect(Collectors.toMap(item -> item.getDynamicId().toString(), DynamicCountDTO::getCount));
        redisCache.setCacheMap(CacheConstants.STORE_PREFIX, storeMap);
        // 获取动态对应的评论数
        List<DynamicCountDTO> commentsCountByDynamicIds = commentsMapper.dynamicCommentCountByDynamicIds(ids);
        Map<String, Integer> commentMap = commentsCountByDynamicIds.stream().collect(Collectors.toMap(item -> item.getDynamicId().toString(), DynamicCountDTO::getCount));
        redisCache.setCacheMap(CacheConstants.COMMENTS_PREFIX, commentMap);

        // 利用redis 中 hash 数据类型来缓存动态的浏览量
        Map<String, Integer> hitsMap = dynamics.stream().collect(Collectors.toMap(key -> key.getId().toString(), Dynamic::getHits));
        redisCache.setCacheMap(CacheConstants.HITS_PREFIX, hitsMap);
        log.info("初始化动态缓存完成");

    }


    /**
     * 查询内容管理列表
     *
     * @param query    查询条件
     * @param pageNum  页码
     * @param pageSize 也大小
     * @return TableDataInfo<DynamicDoc>
     */
    @Override
    public TableDataInfo selectDynamicDocList(DynamicQuery query, Integer pageNum, Integer pageSize) {
        // 分页查询
        EsPageInfo<DynamicDoc> esPageInfo = dynamicEsMapper.pageQuery(buildQueryWrapper(query), pageNum, pageSize);

        // 构建返回值
        List<DynamicDoc> list = esPageInfo.getList();
        if (CollUtil.isEmpty(list)) {
            TableDataInfo.empty();
        }

        // 获取动态对应的对应的标签信息
        Map<Long, Tag> tagMap = getTagMap(list);
        if (CollUtil.isEmpty(tagMap)) {
            TableDataInfo.success(esPageInfo.getTotal(), list);
        }

        // 为每个doc添加上tags信息
        for (DynamicDoc doc : list) {
            List<Long> tagIds = doc.getTagIds();
            if (CollUtil.isNotEmpty(tagIds)) {
                doc.setTags(tagIds.stream().map(tagMap::get).collect(Collectors.toList()));
            }
            // 设置浏览量
            doc.setHits(getCacheHits(doc.getId()));
        }

        // 封装返回值
        return TableDataInfo.success(esPageInfo.getTotal(), list);
    }

    /**
     * 获取内容管理详细信息
     *
     * @param id      id
     * @param isAdmin
     * @return DynamicVO
     */
    @Override
    public DynamicVO getDetailsById(Long id, boolean isAdmin) {
        Dynamic dynamic = getById(id);
        if (dynamic == null) throw new ServiceException("内容不存在");

        // 如果不是管理员，并且动态，不是自己能查看
        if (!isAdmin) {
            Long userId = SecurityUtils.getMobileUserId();
            if (!dynamic.getCreateUserId().equals(userId)) {
                throw new ServiceException("没有权限查看");
            }
        }

        // 封装 vo 返回
        DynamicVO vo = BeanUtil.copyProperties(dynamic, DynamicVO.class);

        // 设置其他信息
        setOtherInfo(dynamic.getId(), vo, isAdmin);
        return vo;
    }

    /**
     * 设置其他信息： 1. 用户信息 2. 标签信息 3. 浏览量 4. 收藏数量 5. 评论数量
     *
     * @param id 动态id
     * @param vo 返回的vo 数据
     */
    private void setOtherInfo(Long id, DynamicVO vo, boolean isAdmin) {
        if (!isAdmin) {
            // 查询用户信息
            Long userId = vo.getCreateUserId();
            MobileUser user = userService.getById(userId);
            vo.setNickname(user.getNickName());
            vo.setAvatar(user.getAvatar());
        }

        // 查询标签信息
        List<Tag> tags = tagMapper.selectTagsByDynamicId(id);
        vo.setTags(tags);

        // 设置浏览量
        vo.setHits(getCacheHits(id));

        // 统计收藏数量
        vo.setStoreCount(getCacheStore(id));

        // 统计评论数量
        vo.setCommentCount(getCacheComment(id));
    }


    /**
     * 更新状态
     *
     * @param id               主键
     * @param verifyStatusEnum 状态
     * @param remark
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Long id, VerifyStatusEnum verifyStatusEnum, String remark) {
        // 检查数据
        Dynamic content = getById(id);
        if (content == null) throw new ServiceException("内容不存在");

        // 更新 mysql
        String username = SecurityUtils.getUsername();
        LocalDateTime now = LocalDateTime.now();
        content.setVerifyStatus(verifyStatusEnum);
        content.setUpdateBy(username);
        content.setUpdateTime(now);
        if (verifyStatusEnum.equals(VerifyStatusEnum.VERIFY_NOT_PASS)) {
            content.setRemark(remark);
        }
        boolean b = updateById(content);
        if (!b) throw new ServiceException("更新失败");

        // 更新 es
        DynamicDoc dynamicDoc = dynamicEsMapper.selectById(id);
        if (dynamicDoc == null) {
            // 新增, 发送消息
            AddDynamicDocMsg addDynamicDocMsg = new AddDynamicDocMsg();
            addDynamicDocMsg.setId(content.getId());
            // 查询需要新增动态的对应的标签信息
            List<DynamicTag> tags = dynamicTagService.list(
                    Wrappers.<DynamicTag>lambdaQuery()
                            .eq(DynamicTag::getDynamicId, content.getId())
            );
            if (CollUtil.isNotEmpty(tags)) {
                addDynamicDocMsg.setTagIds(tags.stream().map(DynamicTag::getTagId).collect(Collectors.toList()));
            }
            dynamicEsService.sendAddDynamicMessage(addDynamicDocMsg);
        } else {
            // 更新状态
            dynamicDoc.setVerifyStatus(verifyStatusEnum.getCode());
            Integer result = dynamicEsMapper.updateById(dynamicDoc);
            if (result == 0) throw new ServiceException("更新 es  失败");
        }
    }

    /**
     * 删除动态
     *
     * @param ids 动态id 集合
     */
    @Override
    @Transactional
    public void delByIds(List<Long> ids) {
        boolean b = removeByIds(ids);
        if (!b) throw new ServiceException("删除失败");

        // 删除其他附信息 1. 收藏 2. 评论 3. es
        dynamicEsService.sendDeleteDynamicDoc(ids);
    }

    /**
     * 添加动态
     *
     * @param dto DynamicDTO
     */
    @Override
    public void addDynamic(DynamicDTO dto) {
        // 1. 保存db 数据
        Dynamic dynamic = BeanUtil.copyProperties(dto, Dynamic.class);
        LoginMobileUser loginUser = SecurityUtils.getMobileLoginUser();
        dynamic.setCreateUserId(loginUser.getUserId());
        dynamic.setCreateBy(loginUser.getUsername());
        dynamic.setUpdateBy(loginUser.getUsername());
        dynamic.setHits(0);
        dynamic.setVerifyStatus(VerifyStatusEnum.WAIT_VERIFY);
        boolean b = save(dynamic);
        if (!b) {
            throw new ServiceException("添加动态失败");
        }

        AddDynamicDocMsg addDynamicDocMsg = new AddDynamicDocMsg();
        addDynamicDocMsg.setId(dynamic.getId());
        addDynamicDocMsg.setTagIds(dto.getTagIds());


        // 2. 检查是否存在标签
        if (CollUtil.isNotEmpty(dto.getTagIds())) {
            List<DynamicTag> tags = dto.getTagIds().stream().map(tagId -> new DynamicTag(dynamic.getId(), tagId)).collect(Collectors.toList());
            dynamicTagService.saveBatch(tags);
        }


        // 2. 发送消息通知 es 添加动态
        dynamicEsService.sendAddDynamicMessage(addDynamicDocMsg);

    }

    /**
     *修改动态，注意点：动态一定修改，就需要动态重置到待审核状态
     * @param dto
     *
     */
    /**
     * 修改动态
     *
     * @param dto DynamicDTO
     */
    @Override
    @Transactional
    public void updateDynamic(DynamicDTO dto) {
        // 1. 检查数据
        Long dynamicId = dto.getId();
        LoginMobileUser mobileLoginUser = SecurityUtils.getMobileLoginUser();
        Dynamic one = lambdaQuery()
                .eq(Dynamic::getId, dynamicId)
                .eq(Dynamic::getCreateUserId, mobileLoginUser.getUserId())
                .one();
        if (one == null) {
            throw new ServiceException("动态不存在");
        }
        if (one.getVerifyStatus().equals(VerifyStatusEnum.VERIFY_NOT_PASS)) {
            throw new ServiceException("动态审核未通过，无法修改");
        }


        Dynamic dynamic = BeanUtil.copyProperties(dto, Dynamic.class);
        dynamic.setUpdateBy(mobileLoginUser.getUsername());
        dynamic.setUpdateTime(LocalDateTime.now());
        dynamic.setVerifyStatus(VerifyStatusEnum.WAIT_VERIFY);
        // 2. 更新数据
        boolean b = updateById(dynamic);
        if (!b) {
            throw new ServiceException("修改动态失败");
        }

        // 3.  修改标签，1. 将原来的标签删除  2 新增标签
        dynamicTagService.remove(
                Wrappers.<DynamicTag>lambdaQuery()
                        .eq(DynamicTag::getDynamicId, dynamicId)
        );

        if (CollUtil.isNotEmpty(dto.getTagIds())) {
            // 封装出 DynamicTag 对象
            List<DynamicTag> tags = dto.getTagIds().stream()
                    .map(tagId -> new DynamicTag(dynamicId, tagId))
                    .collect(Collectors.toList());
            // 新增标签
            dynamicTagService.saveBatch(tags);
        }


        // 4. 更新 es
        dynamicEsService.updateDynamicDoc(dynamic, dto.getTagIds());


    }

    /**
     * 查询动态列表
     *
     * @param query DynamicQuery
     * @return PageDTO<DynamicVO>
     */
    @Override
    public PageDTO<DynamicVO> selectDynamicVOList(DynamicQuery query) {
        EsPageInfo<DynamicDoc> esPageInfo = dynamicEsMapper.pageQuery(buildQueryWrapper(query), query.getPageNo(), query.getPageSize());
        List<DynamicDoc> list = esPageInfo.getList();
        if (CollUtil.isEmpty(list)) {
            return PageDTO.empty(0L, 0L);
        }
        // 1. 查询用户信息
        Map<Long, MobileUser> userMap = getLongUserMap(list);

        // 2. 查询标签信息
        Map<Long, Tag> tagMap = getTagMap(list);

        // 4. 封装VO数据
        List<DynamicVO> vos = getDynamicVOS(list, userMap, tagMap);

        // 封装分页信息返回
        return PageDTO.of(esPageInfo.getTotal(), (long) esPageInfo.getPages(), vos);
    }

    /**
     * 获取审核通过详情
     *
     * @param id 动态id
     * @return DynamicVO
     */
    @Override
    public DynamicVO getPassedDetailsById(Long id) {
        Dynamic dynamic = lambdaQuery()
                .eq(Dynamic::getId, id)
                .eq(Dynamic::getVerifyStatus, VerifyStatusEnum.VERIFY_PASS)
                .one();
        if (dynamic == null) throw new ServiceException("内容不存在");

        DynamicVO vo = BeanUtil.copyProperties(dynamic, DynamicVO.class);
        vo.setUpdateBy(null);
        vo.setCreateBy(null);

        // 设置其他信息
        setOtherInfo(dynamic.getId(), vo, false);
        return vo;
    }

    /**
     * 批量修改浏览量
     *
     * @param dynamicList 动态数据，有每个对象中有 id 和 hits
     */
    @Override
    @Transactional
    public void batchUpdateHits(List<Dynamic> dynamicList) {
        if (CollUtil.isNotEmpty(dynamicList)) {
            updateBatchById(dynamicList);
        }
    }

    /**
     * 动态状态统计
     * @return
     */
    @Override
    public List<StatusCountDTO> dynamicCount() {
       return getBaseMapper().countVerifyStatus();
    }


    /**
     * 获取用户信息
     *
     * @param list doc 文档列表
     * @return
     */
    private Map<Long, MobileUser> getLongUserMap(List<DynamicDoc> list) {
        Map<Long, MobileUser> userMap = new HashMap<>();
        // 1.查询用户数据
        List<Long> userIds = list.stream().map(DynamicDoc::getCreateUserId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty((userIds))) {
            List<MobileUser> users = userService.list(
                    Wrappers.<MobileUser>lambdaQuery()
                            .in(MobileUser::getUserId, userIds)
            );
            userMap = users.stream().collect(Collectors.toMap(MobileUser::getUserId, item -> item));
        }
        return userMap;
    }

    /**
     * 获取标签信息
     *
     * @param list doc 文档列表
     * @return
     */
    private Map<Long, Tag> getTagMap(List<DynamicDoc> list) {
        HashSet<Long> selectedTagIds = new HashSet<Long>();
        list.forEach(item -> {
            List<Long> tagIds = item.getTagIds();
            if (CollUtil.isNotEmpty(tagIds)) {
                selectedTagIds.addAll(tagIds);
            }
        });
        Map<Long, Tag> tagMap = new HashMap<>();
        if (CollUtil.isNotEmpty(selectedTagIds)) {
            // 查询tag信息
            List<Tag> tags = tagMapper.selectBatchIds(selectedTagIds);
            // tag 和 tagId 映射
            tagMap = tags.stream().collect(Collectors.toMap(Tag::getId, tag -> tag));
        }
        return tagMap;
    }

    /**
     * 封装返回列表
     *
     * @param list    doc 文档列表
     * @param userMap 用户信息
     * @param tagMap  标签信息
     * @return List<DynamicVO>
     */
    private List<DynamicVO> getDynamicVOS(List<DynamicDoc> list, Map<Long, MobileUser> userMap, Map<Long, Tag> tagMap) {
        List<Long> storeIdsByUserId = new ArrayList<>();
        Long userId = null;
        try {
            userId = SecurityUtils.getMobileUserId();
            if (userId != null) {
                storeIdsByUserId = storeMapper.selectStoreIdsByUserId(userId);
            }
        } catch (Exception e) {

        }
        List<DynamicVO> vos = BeanUtil.copyToList(list, DynamicVO.class);
        for (DynamicVO vo : vos) {
            // 设置浏览量
            vo.setHits(getCacheHits(vo.getId()));
            // 设置评论数量
            vo.setCommentCount(getCacheComment(vo.getId()));
            // 设置收藏数量
            vo.setStoreCount(getCacheStore(vo.getId()));

            // 设置否收藏
            if (userId != null) {
                vo.setIsStore(storeIdsByUserId.contains(vo.getId()));
            }
            // 隐藏部分信息
            vo.setCreateBy(null);
            vo.setUpdateBy(null);

            // 设置用户信息
            MobileUser user = userMap.get(vo.getCreateUserId());
            if (user != null) {
                vo.setNickname(user.getNickName());
                vo.setAvatar(user.getAvatar());
            }

            // 设置标签信息
            List<Long> tagIds = vo.getTagIds();
            if (CollUtil.isNotEmpty(tagIds)) {
                List<Tag> tagList = tagIds.stream().map(tagMap::get).collect(Collectors.toList());
                vo.setTags(tagList);
            }

        }
        return vos;
    }


    /**
     * 构建查询条件
     *
     * @param query 查询条件
     * @return LambdaEsQueryWrapper
     */
    private LambdaEsQueryWrapper<DynamicDoc> buildQueryWrapper(DynamicQuery query) {
        LambdaEsQueryWrapper<DynamicDoc> wrapper = new LambdaEsQueryWrapper<>();

        // 创建人
        wrapper.eq(query.getCreateUserId() != null, DynamicDoc::getCreateUserId, query.getCreateUserId());

        // 状态值
        wrapper.eq(query.getVerifyStatus() != null && query.getVerifyStatus() >= 0, DynamicDoc::getVerifyStatus, query.getVerifyStatus());

        // 关键字
        String keyword = query.getKeyword();
        if (StrUtil.isNotBlank(keyword)) {
            wrapper.match(DynamicDoc::getContent, keyword);
        }

        // 时间范围
        if (query.getBeginTime() != null && query.getEndTime() != null) {
            // 手动时间转换 es 支持的时间格式字符串，否则会报错
            String beginTime = query.getBeginTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            String endTime = query.getEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            wrapper.between(StrUtil.isNotBlank(beginTime) && StrUtil.isNotBlank(endTime), DynamicDoc::getCreateTime, beginTime, endTime);

        }


        // 标签集合
        wrapper.in(query.getTagIds() != null && !query.getTagIds().isEmpty(), DynamicDoc::getTagIds, query.getTagIds());

        // 排序
        wrapper.orderByDesc(DynamicDoc::getHits).orderByDesc(DynamicDoc::getCreateTime);

        return wrapper;
    }

    /**
     * 获取缓存中浏览数量
     *
     * @param id dynamicId
     * @return 浏览数量
     */
    public Integer getCacheHits(Long id) {
        Object cacheMapValue = redisCache.getCacheMapValue(CacheConstants.HITS_PREFIX, id.toString());
        if (cacheMapValue != null) {
            return (Integer) cacheMapValue;
        }
        return 0;
    }


    /**
     * 获取缓存中收藏数量
     *
     * @param id dynamicId
     * @return 收藏数量
     */
    public Integer getCacheStore(Long id) {
        Object cacheMapValue = redisCache.getCacheMapValue(CacheConstants.STORE_PREFIX, id.toString());
        if (cacheMapValue != null) {
            return (Integer) cacheMapValue;
        }
        return 0;
    }


    /**
     * 获取缓存中评论数量
     *
     * @param id dynamicId
     * @return 评论数
     */
    public Integer getCacheComment(Long id) {
        Object cacheMapValue = redisCache.getCacheMapValue(CacheConstants.COMMENTS_PREFIX, id.toString());
        if (cacheMapValue != null) {
            return (Integer) cacheMapValue;
        }
        return 0;
    }

}
